#!/usr/bin/python

import irclib, logging, logging.handlers, threading, time
import modules
from modules import *
import config

# GLOBAL CONSTANTS

MSGDELAY = 3 # seconds 
MAXMSGSIZE = 300 # chars
AUTHDELAY = 5 # seconds
RECONNECTDELAY = 10
DEFAULT_IRC_PORT = 6667


class Botija(irclib.SimpleIRCClient):

	def __init__(self, network, log):
		irclib.SimpleIRCClient.__init__(self)
		self.ircnetwork = network
		self.current_chans = set()
		self.log = log
		self.lock = threading.Lock()
		self.log.debug('Botija initialized')
		self._retryConnect()
	
	"""
	EVENT FUNCTIONS
	"""
	def on_welcome(self, connection, event):
		self.log.debug('welcome event')
		self.ircnetwork.auth(self.connection)
		time.sleep(AUTHDELAY);
		self.ircnetwork.hideIP(self.connection)
		self._joinChans()
		self._dispatchEvent(connection, event, 'welcome')
		
	def on_kick(self, connection, event):
		self.log.debug('kick event on chan %s with arguments %s' % (str(event.target()), str(event.arguments())))
		if event.arguments()[0] == connection.get_nickname() :
			self.log.info('You were kicked from %s by %s - Reason: %s' % (str(event.target()), str(event.source()), str(event.arguments()[1])))
			self.current_chans.remove(str(event.target()))
			key = None
			if str(event.target()) in config.TARGETS.keys():
				key = config.TARGETS[str(event.target())]
			t = threading.Thread(target=self._rejoinChannel, args=(str(event.target()), key, self.log, connection, ))
			t.start()
		self._dispatchEvent(connection, event, 'kick')

	def on_join(self, connection, event):
		self.log.debug('Join event: %s joined %s' % ( str(event.source()),str(event.target()) ))
		nick = event.source().split('!', 1)[0]
		if nick == connection.get_nickname() :
			self.log.info('Joined channel: %s' % (event.target()))
			self.current_chans.add(str(event.target()))
			self.log.debug('Current chans: %s' % (str(self.current_chans)))
		self._dispatchEvent(connection, event, 'join')

	def on_quit(self, connection, event):
		self.log.debug('Quit event: %s quitted %s' % ( str(event.source()),str(event.target()) ))
		nick = event.source().split('!', 1)[0]
		if nick == connection.get_nickname() :
			pass
		self._dispatchEvent(connection, event, 'quit')			
	
	def on_disconnect(self, connection, event):
		self.log.info('Disconnected')
		time.sleep(RECONNECTDELAY)
		self._retryConnect()
		self._dispatchEvent(connection, event, 'disconnect')

		
	def on_pubmsg(self, connection, event):
		self.log.debug('PUBMSG %s: %s says %s' % ( str(event.target()), str(event.source()),str(event.arguments())  ))
		msg = "".join(event.arguments())
		command = None
		if msg.find('!') == 0:
			command = msg.split()[0][1:]
		if command != None:
			self._dispatchEvent(connection, event, command)
		else: self._dispatchEvent(connection, event, 'pubmsg')
				

	"""
	PUBLIC FUNCTIONS
	"""
	def sendMsg(self, target, line):
		self.log.debug('sendMsg called')
		if len(line) > MAXMSGSIZE:
			self.connection.privmsg(target,line[0:MAXMSGSIZE])
			time.sleep(MSGDELAY);
			self.sendMsg(target, line[MAXMSGSIZE:])
		else: 
			self.connection.privmsg(target,line)
			time.sleep(MSGDELAY)
	def log(self, msg):
		self.log.info(msg);
			
		
	"""
	PRIVATE FUNCTIONS
	"""
	
	def _dispatchEvent(self, connection, event, strevent):
		self.log.debug('Dispatching %s event' % (strevent))
		for module in dir(modules):
			try:
				try:
					func = getattr(getattr(modules,module), "on_"+strevent)
				except AttributeError:
					continue
				self.log.debug('Delivered %s event to module: %s' %(strevent,module))
				t = threading.Thread(target=func, args=(connection,event,self, ))
				t.start()
				#func(connection, event, self)
			except Exception, e:
				self.log.error('Error while calling %s.on_%s callback' % (module, strevent))
				self.log.error(str(e))
				continue;	
		
	def _joinChans(self):
		self.log.debug('Rejoining channels')
		for key in config.TARGETS.keys():
			if irclib.is_channel(key):
				self.connection.join(key, config.TARGETS[key])
				
	def _rejoinChannel(self, chan, key, log, connection):
		log.info('Trying to rejoin channel %s' % (chan))	
		log.debug('Current chans: %s' % (str(self.current_chans)))
		while (chan not in self.current_chans):
			connection.join(chan, key)
			time.sleep(10)
		log.info('Successfully rejoined channel %s' % (chan));
				
	def _retryConnect(self):
		while (True):
			try:
				server_data = self.ircnetwork.getServer().split(':', 1)
				if len(server_data)>1: port = int(server_data[1])
				else: port = int(DEFAULT_IRC_PORT)
				self.log.info('Connecting to %s:%i' % (server_data[0],port))
				#self.connect(server_data[0], port, self.ircnetwork.getNick(), "", "", "", localaddress="87.98.227.68")
				self.connect(server_data[0], port, self.ircnetwork.getNick())
				self.log.info('Connection succesful')
				break;
			except irclib.ServerConnectionError, x:
				self.log.error('Error while connecting: %s' %(x))


def main():
	logging.basicConfig(format=config.LOGFORMAT)
	log = logging.getLogger('Botija')
	log.setLevel(config.LOGLEVEL)
	handler = logging.handlers.RotatingFileHandler(config.LOGFILE, maxBytes=1000000, backupCount=3)
	formatter = logging.Formatter(config.LOGFORMAT)
	handler.setFormatter(formatter)
	log.addHandler(handler)
	b = Botija(config.NETWORK, log)
	b.start()



if __name__ == "__main__":
	try:
		main()
	except (KeyboardInterrupt, SystemExit):
		print "Keyboard Interrupt: exiting"
		raise
			
